/* 
 * This file is part of the Coffee Survey Framework - http://coffee.codeplex.com
 */
using System;
using System.Web.UI;
using System.Reflection;
using System.ComponentModel;
using Coffee.ServiceLocators;

namespace Coffee
{
    /// <summary>
    /// Summary description for Reflection
    /// </summary>
    public class Reflection
    {
        public static object GetProperty(string propertyName, object target)
        {
            PropertyDescriptorCollection props = null;
            PropertyDescriptor property = null;
            props = TypeDescriptor.GetProperties(target.GetType());
            property = props[propertyName];
            return property.GetValue(target);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="target"></param>
        /// <param name="value"></param>
        public static void SetProperty(string propertyName, object target, object value)
        {
            PropertyDescriptorCollection props = null;
            PropertyDescriptor property = null;
            props = TypeDescriptor.GetProperties(target.GetType());
            property = props[propertyName];

            ValueConverterDelegate converter = ValueConverterService.ResolveProvider(property.PropertyType);
            if (converter != null)
                value = converter(value);

            property.SetValue(target, value);
        }

        public static void SetProperty(string propertyName, object target, object index, object value)
        {
            target.GetType().GetProperty(propertyName).SetValue(target, value, new object[] { index });
        }

        public static object CallMethod(string className, string methodName, object[] parameters)
        {
            try
            {
                Type type = TypeFactory.ResolveType(className);

                MethodInfo meth = type.GetMethod(methodName);
                if (meth == null)
                {
                    throw new Exception("Method not found");
                }

                if (parameters != null)
                {
                    ParameterInfo[] paraminfos = meth.GetParameters();

                    if (parameters.Length < paraminfos.Length)
                    {
                        object[] newparams = new object[paraminfos.Length];
                        for (int i = 0; i < parameters.Length; i++)
                        {
                            newparams[i] = parameters[i];
                        }
                        parameters = newparams;
                    }

                    for (int i = 0; i < paraminfos.Length; i++)
                    {
                        ParameterInfo paraminfo = paraminfos[i];

                        if (parameters[i] != null)
                        {
                            if (parameters[i].GetType() == typeof(string))
                            {
                                if (paraminfo.ParameterType == typeof(int))
                                {
                                    parameters[i] = int.Parse((string)parameters[i]);
                                }
                            }
                        }
                    }
                }

                try
                {
                    return meth.Invoke(null, parameters);
                }
                catch (System.Reflection.TargetParameterCountException tpcex)
                {
                    throw new Exception(string.Format("Parameters passed do not match any method declaration"));
                }

            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Failed to invoke method {1} of type '{0}' : {2}", className, methodName, ex.Message), ex);
            }
        }

        private static Type GetType(Assembly assembly, string className)
        {
            Type type = assembly.GetType(className);
            if (type != null)
            {
                return type;
            }
            else
            {
                throw new Exception(string.Format("Type {0} does not exist in assembly", className));
            }
        }

        private static Type GetType(string className)
        {
            return GetType(Assembly.GetExecutingAssembly(), className);
        }

        public static object CallMethod(string className, string methodName)
        {
            try
            {
                Type type = GetType(className);
                MethodInfo mInfo = type.GetMethod(methodName);
                if (mInfo != null)
                {
                    return mInfo.Invoke(null, null);
                }
                else
                {
                    throw new Exception("Method not found");
                }
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Failed to invoke method {1} of type '{0}' : {2}", className, methodName, ex.Message), ex);
            }
        }

        public static object CreateLocalObject(string className)
        {
            try
            {
                Type type = GetType(className);
                return type.GetConstructor(new Type[] { }).Invoke(new Object[] { });
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Failed to invoke constructor of type '{0}' : {1}", className, ex.Message), ex);
            }
        }

        public static object CreateLocalObject(Assembly assembly, string className)
        {
            try
            {
                Type type = GetType(assembly, className);
                return type.GetConstructor(new Type[] { }).Invoke(new Object[] { });
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Failed to invoke constructor of type '{0}' : {1}", className, ex.Message), ex);
            }
        }

        public static object CreateLocalObject(Type type)
        {
            try
            {
                return type.GetConstructor(new Type[] { }).Invoke(new Object[] { });
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Failed to invoke constructor of type '{0}' : {1}", type.Name, ex.Message), ex);
            }
        }

        public static object CreateLocalObject(string className, Type[] typeList, object[] paramList)
        {
            try
            {
                Type type = GetType(className);
                ConstructorInfo cInfo = type.GetConstructor(typeList);
                if (cInfo != null)
                {
                    return cInfo.Invoke(paramList);
                }
                else
                {
                    string[] stypelist = new string[typeList.Length];
                    int i = 0;
                    foreach (Type t in typeList)
                    {
                        stypelist[i++] = t.Name;
                    }
                    throw new Exception(string.Format("Could not find a matching constructor with parameters ({0})", string.Join(", ", stypelist)));
                }
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Failed to invoke constructor of type '{0}' : {1}", className, ex.Message), ex);
            }
        }


        public static object CreateLocalObject(Type type, Type[] typeList, object[] paramList)
        {
            try
            {
                ConstructorInfo cInfo = type.GetConstructor(typeList);
                if (cInfo != null)
                {
                    return cInfo.Invoke(paramList);
                }
                else
                {
                    string[] stypelist = new string[typeList.Length];
                    int i = 0;
                    foreach (Type t in typeList)
                    {
                        stypelist[i++] = t.Name;
                    }
                    throw new Exception(string.Format("Could not find a matching constructor with parameters ({0})", string.Join(", ", stypelist)));
                }
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Failed to invoke constructor of type '{0}' : {1}", type.Name, ex.Message), ex);
            }
        }


        public static object CreateLocalObject(Assembly assembly, string className, Type[] typeList, object[] paramList)
        {
            try
            {
                Type type = GetType(assembly, className);
                ConstructorInfo cInfo = type.GetConstructor(typeList);
                if (cInfo != null)
                {
                    return cInfo.Invoke(paramList);
                }
                else
                {
                    string[] stypelist = new string[typeList.Length];
                    int i = 0;
                    foreach (Type t in typeList)
                    {
                        stypelist[i++] = t.Name;
                    }
                    throw new Exception(string.Format("Could not find a matching constructor with parameters ({0})", string.Join(", ", stypelist)));
                }
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Failed to invoke constructor of type '{0}' : {1}", className, ex.Message), ex);
            }
        }

        public static string GetPostData(Page page, string controlname)
        {
            foreach (string key in page.Request.Form.AllKeys)
            {
                if (key.Contains("$" + controlname))
                {
                    return page.Request.Form[key];
                }
            }
            return "";
        }
    }
}